<script lang="ts">
    import { createUniqueId } from "../utils/createUniqueId";
    import { useClassList } from "../utils/useClassList";
    import type { IndexItemData, IndexListProps, IndexStore } from "./IndexList";

    const { value = $bindable([]), ...props }: IndexListProps = $props();
    const promote = $derived(props.promote ?? true);
    let activeAnchor = $state("");
    let showPromote = $state(false);
    let promoteText = $state("");

    const store = $state<IndexStore>({
        list: [],
        listMap: {},
    });

    // 构建数据结构
    let map: { [key: string | number]: any } = {};
    let wrap: any;
    const topMap: { [key: string | number]: number } = {};

    $effect(() => {
        const list: IndexItemData[] = [];
        map = {};
        props.data.forEach((item: any) => {
            if (item.id === undefined || item.id === null) {
                item.id = createUniqueId();
            }
            const newItem: any = { id: item.id };
            map[item.id] = item;
            list.push(newItem);
            if (item.children) {
                newItem.children = [];
                item.children.forEach((subItem: any) => {
                    if (subItem.id === undefined || subItem.id === null) {
                        subItem.id = createUniqueId();
                    }
                    map[subItem.id] = subItem;
                    const newSubItem = { id: subItem.id } as IndexItemData;
                    newItem.children.push(newSubItem);
                });
            }
        });
        store.list = list;
    });

    const classList = $derived(
        useClassList(props, "cm-index-list", {
            "cm-index-list-border": props.border,
        })
    );

    const onItemClick = (subItem: any) => {
        if (!props.selectable) {
            return;
        }
        const id = subItem.id;

        if (subItem.active) {
            const index = value.indexOf(id);
            value.splice(index, 1);
        } else {
            value.push(id);
        }

        props.onchange?.($state.snapshot(value));
        subItem.active = !subItem.active;
    };

    let promoteTimer: any = null;

    const gotoAnchor = (id: string, name: string, e: Event) => {
        if (e.preventDefault) {
            e.preventDefault();
        }
        if (e.stopPropagation) {
            e.stopPropagation();
        }
        const ele = document.querySelector(id);

        if (ele) {
            if (promote) {
                promoteText = name;
                showPromote = true;
                if (promoteTimer) {
                    clearTimeout(promoteTimer);
                }
                promoteTimer = setTimeout(() => {
                    scrollEnd();
                }, 1000);
            }
            const eleOff = ele.getBoundingClientRect().top;
            const parentOff = wrap.getBoundingClientRect().top;
            const scrollTop = eleOff - parentOff;
            wrap.scrollTo({
                top: wrap.scrollTop + scrollTop,
                behavior: "smooth",
            });
        }
    };

    const scrollEnd = () => {
        showPromote = false;
    };

    const handleScroll = () => {
        const scrollTop = wrap.scrollTop;
        const id = getAnchorByScrollTop(scrollTop);
        activeAnchor = id;
    };

    const getAnchorByScrollTop = (scrollTop: number) => {
        let minId = "";
        let min = Number.MAX_VALUE;
        for (const id in topMap) {
            const t = Math.abs(topMap[id] - scrollTop);
            if (min > t) {
                min = t;
                minId = id;
            }
        }
        return minId;
    };

    const initTop = (e: HTMLElement, id: string) => {
        queueMicrotask(() => {
            topMap[id] = e.offsetTop;
        });
    };

    const promoteClass = $derived({
        "cm-index-list-promote": true,
        "cm-index-list-promote-show": showPromote,
    });
</script>

<div class={classList} style={props.style}>
    <div class="cm-index-list-list" bind:this={wrap} onscroll={handleScroll}>
        {#each store.list as item}
            {@const dataItem = map[item.id]}
            <dl id={`cm_index_list_${item.id}`} use:initTop={item.id}>
                <dt>{dataItem.name}</dt>
                {#if item.children}
                    {#each item.children as subItem}
                        {@const subDataItem = map[subItem.id]}
                        <!-- svelte-ignore a11y_click_events_have_key_events -->
                        <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
                        <dd class={subItem.active ? "active" : ""} onclick={onItemClick.bind(null, subItem)}>
                            {#if props.renderItem}
                                {@render props.renderItem(subDataItem, subItem.active)}
                            {:else}
                                {subDataItem.name}
                            {/if}
                        </dd>
                    {/each}
                {/if}
            </dl>
        {/each}
    </div>
    <div class="cm-index-list-nav">
        {#each store.list as item}
            {@const dataItem = map[item.id]}
            {@const active = activeAnchor === item.id}
            {@const classList = {
                "cm-index-list-nav-item": true,
                active: active,
            }}
            <!-- svelte-ignore a11y_click_events_have_key_events -->
            <!-- svelte-ignore a11y_no_static_element_interactions -->
            <div class={classList} onclick={gotoAnchor.bind(null, `#cm_index_list_${item.id}`, dataItem.id)}>
                {dataItem.id}
            </div>
        {/each}
    </div>
    {#if promote}
        <div class={promoteClass}>
            {promoteText}
        </div>
    {/if}
</div>
